package random;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Random;

public final class RandomWalkFast {

	public int size = 256;

	public final Stats calculateLengthASawShort(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return calculateLengthASaw(stepSizes, map);
	}

	public final Stats calculateLengthASawLong(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return calculateLengthASaw(stepSizes, map);
	}

	private final Stats calculateLengthASaw(final int[] stepSizes, final ASawGrid map) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		// map.SUB >>>=s/2;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;

		final Random r = new Random();
		// final Random r = new MTRandom();
		// final Random r = new MersenneTwister();
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		final int sLength = stepSizes.length;
		int stepSize = stepSizes[step % sLength];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = 0;
		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[step % sLength];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	public final Stats calculateLengthASawShortHT1(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return calculateLengthASawHT1(stepSizes, map);
	}

	public final Stats calculateLengthASawLongHT1(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return calculateLengthASawHT1(stepSizes, map);
	}

	private final Stats calculateLengthASawHT1(final int[] stepSizes, final ASawGrid map) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;
		int x2 = xInit;
		int y2 = yInit;

//		final Random r = new Random();
		final Random r = new MTRandom();
		// final Random r = new MersenneTwister();
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		int i = 0;
		final int sLength = stepSizes.length;
		int stepSize = stepSizes[i % sLength];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = 0;

		int i2 = sLength - 1;
		int lastDir2 = r.nextInt(4);
		int dirMask2 = 15 & ~(1 << lastDir2);
		int stepSize2 = stepSizes[i2];
		int stepDir2 = 0;
		while (dirMask != 0 || dirMask2 != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize) && (x - stepSize != x2 || y != y2)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize) && (x != x2 || y - stepSize != y2)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize) && (x + stepSize != x2 || y != y2)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize) && (x != x2 || y + stepSize != y2)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[++i % sLength];
			}

			loop2: while (dirMask2 != 0) {
				stepDir2 = random(lastDir2, dirMask2, r, 4);
				switch (stepDir2) {
					case 0:
						if (!map.intersectV(x2, y2, -stepSize2) && (x2 - stepSize2 != x || y2 != y)) {
							map.addH(x2, y2, -stepSize2);
							x2 -= stepSize2;
							break loop2;
						}
						break;
					case 1:
						if (!map.intersectH(x2, y2, -stepSize2) && (x2 != x || y2 - stepSize2 != y)) {
							map.addV(x2, y2, -stepSize2);
							y2 -= stepSize2;
							break loop2;
						}
						break;
					case 2:
						if (!map.intersectV(x2, y2, stepSize2) && (x2 + stepSize2 != x || y2 != y)) {
							map.addH(x2, y2, stepSize2);
							x2 += stepSize2;
							break loop2;
						}
						break;
					case 3:
						if (!map.intersectH(x2, y2, stepSize2) && (x2 != x || y2 + stepSize2 != y)) {
							map.addV(x2, y2, stepSize2);
							y2 += stepSize2;
							break loop2;
						}
						break;
				}
				// only when no step was taken
				dirMask2 &= ~(1 << (stepDir2));
			}
			if (dirMask2 != 0) {
				lastDir2 = (stepDir2 + 2) % 4;
				dirMask2 = 15 & ~(1 << lastDir2);
				step++;
				i2 = (i2 + sLength - 1) % sLength;
				length += stepSize2;
				stepSize2 = stepSizes[i2];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	public final Stats calculateLengthASawShortHT2(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return calculateLengthASawHT2(stepSizes, map);
	}

	public final Stats calculateLengthASawLongHT2(final int[] stepSizes) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return calculateLengthASawHT2(stepSizes, map);
	}

	private final Stats calculateLengthASawHT2(final int[] stepSizes, final ASawGrid map) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		// map.SUB >>>=s/2;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;

		final Random r = new Random();
		// final Random r = new MTRandom();
		// final Random r = new MersenneTwister();
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		int i = 0;
		final int sLength = stepSizes.length;
		int stepSize = stepSizes[i % sLength];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = random(lastDir, dirMask, r, 4);
		int initStepDir = stepDir;
		{
			switch (stepDir) {
				case 0:
					map.addH(x, y, -stepSize);
					x -= stepSize;
					break;
				case 1:
					map.addV(x, y, -stepSize);
					y -= stepSize;
					break;
				case 2:
					map.addH(x, y, stepSize);
					x += stepSize;
					break;
				case 3:
					map.addV(x, y, stepSize);
					y += stepSize;
					break;
			}
			lastDir = (stepDir + 2) % 4;
			dirMask = 15 & ~(1 << lastDir);
			step++;
			length += stepSize;
			stepSize = stepSizes[++i % sLength];
		}
		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[++i % sLength];
			}
		}
		x = xInit;
		y = yInit;
		i = stepSizes.length - 1;
		lastDir = (initStepDir + 2) % 4;
		dirMask = 15 & ~(1 << lastDir);
		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				i = (i + sLength - 1) % sLength;
				stepSize = stepSizes[i];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	public final Stats drawASawShort(final int[] stepSizes, final WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return drawASaw(stepSizes, map, rend);
	}

	public final Stats drawASawLong(final int[] stepSizes, final WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return drawASaw(stepSizes, map, rend);
	}

	private final Stats drawASaw(final int[] stepSizes, final ASawGrid map, final WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;

		final Random r = new Random();
		// final Random r = new MersenneTwister(System.currentTimeMillis());
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		int stepSize = stepSizes[step % stepSizes.length];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = 0;
		final ArrayDeque<double[]> points = new ArrayDeque<double[]>();
		double[] p = { x, y };
		points.add(p);
		rend.setWalkTail(points.toArray(new double[0][0]));
		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				double[] pTemp = { x, y };
				points.add(pTemp);
				rend.setWalkTail(points.toArray(new double[0][0]));
				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[step % stepSizes.length];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	public final Stats drawASawShortHT1(final int[] stepSizes, WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return drawASawHT1(stepSizes, map, rend);
	}

	public final Stats drawASawLongHT1(final int[] stepSizes, WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return drawASawHT1(stepSizes, map, rend);
	}

	private final Stats drawASawHT1(final int[] stepSizes, final ASawGrid map, final WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;
		int x2 = xInit;
		int y2 = yInit;

		final Random r = new Random();
		// final Random r = new MTRandom();
		// final Random r = new MersenneTwister();
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		int i = 0;
		final int sLength = stepSizes.length;
		int stepSize = stepSizes[i % sLength];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = 0;

		int i2 = sLength - 1;
		int lastDir2 = r.nextInt(4);
		int dirMask2 = 15 & ~(1 << lastDir2);
		int stepSize2 = stepSizes[i2];
		int stepDir2 = 0;

		ArrayDeque<double[]> points = new ArrayDeque<double[]>();
		double[] p = { x, y };
		points.add(p);
		rend.setWalkTail(points.toArray(new double[0][0]));

		ArrayDeque<double[]> points2 = new ArrayDeque<double[]>();
		double[] p2 = { x2, y2 };
		points2.add(p2);
		rend.setWalkHead(points2.toArray(new double[0][0]));

		while (dirMask != 0 || dirMask2 != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize) && (x - stepSize != x2 || y != y2)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize) && (x != x2 || y - stepSize != y2)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize) && (x + stepSize != x2 || y != y2)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize) && (x != x2 || y + stepSize != y2)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {
				double[] pTemp = { x, y };
				points.add(pTemp);
				rend.setWalkTail(points.toArray(new double[0][0]));

				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[++i % sLength];
			}

			loop2: while (dirMask2 != 0) {
				stepDir2 = random(lastDir2, dirMask2, r, 4);
				switch (stepDir2) {
					case 0:
						if (!map.intersectV(x2, y2, -stepSize2) && (x2 - stepSize2 != x || y2 != y)) {
							map.addH(x2, y2, -stepSize2);
							x2 -= stepSize2;
							break loop2;
						}
						break;
					case 1:
						if (!map.intersectH(x2, y2, -stepSize2) && (x2 != x || y2 - stepSize2 != y)) {
							map.addV(x2, y2, -stepSize2);
							y2 -= stepSize2;
							break loop2;
						}
						break;
					case 2:
						if (!map.intersectV(x2, y2, stepSize2) && (x2 + stepSize2 != x || y2 != y)) {
							map.addH(x2, y2, stepSize2);
							x2 += stepSize2;
							break loop2;
						}
						break;
					case 3:
						if (!map.intersectH(x2, y2, stepSize2) && (x2 != x || y2 + stepSize2 != y)) {
							map.addV(x2, y2, stepSize2);
							y2 += stepSize2;
							break loop2;
						}
						break;
				}
				// only when no step was taken
				dirMask2 &= ~(1 << (stepDir2));
			}
			if (dirMask2 != 0) {

				double[] pTemp = { x2, y2 };
				points2.add(pTemp);
				rend.setWalkHead(points2.toArray(new double[0][0]));

				lastDir2 = (stepDir2 + 2) % 4;
				dirMask2 = 15 & ~(1 << lastDir2);
				step++;
				i2 = (i2 + sLength - 1) % sLength;
				length += stepSize2;
				stepSize2 = stepSizes[i2];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	public final Stats drawASawShortHT2(final int[] stepSizes, WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final ASawGrid map = new ASawGridShort(max, localSize);
		return drawASawHT2(stepSizes, map, rend);
	}

	public final Stats drawASawLongHT2(final int[] stepSizes, WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		final YAASawGridLong map = new YAASawGridLong(max, localSize, (max >>> s) / 10);
		return drawASawHT2(stepSizes, map, rend);
	}

	private final Stats drawASawHT2(final int[] stepSizes, final ASawGrid map, WalkRenderer rend) {
		int max = 0;
		for (int i : stepSizes)
			max = i > max ? i : max;
		int s = 2;
		max <<= s;
		final int localSize = this.size >>> s;
		// map.SUB >>>=s/2;
		final int xInit = max * localSize / 2;
		final int yInit = max * localSize / 2;
		map.addH(xInit, yInit, 0);
		map.addV(xInit, yInit, 0);
		int x = xInit;
		int y = yInit;

		final Random r = new Random();
		// final Random r = new MTRandom();
		// final Random r = new MersenneTwister();
		// final MersenneTwisterFast r = new MersenneTwisterFast();
		int length = 0;
		int step = 0;
		int i = 0;
		final int sLength = stepSizes.length;
		int stepSize = stepSizes[i % sLength];
		int lastDir = r.nextInt(4);
		int dirMask = 15 & ~(1 << lastDir);
		int stepDir = random(lastDir, dirMask, r, 4);
		int initStepDir = stepDir;

		final ArrayDeque<double[]> points = new ArrayDeque<double[]>();
		double[] p = { x, y };
		points.add(p);
		rend.setWalkTail(points.toArray(new double[0][0]));

		{
			switch (stepDir) {
				case 0:
					map.addH(x, y, -stepSize);
					x -= stepSize;
					map.addV(x, y, 0);
					break;
				case 1:
					map.addV(x, y, -stepSize);
					y -= stepSize;
					map.addH(x, y, 0);
					break;
				case 2:
					map.addH(x, y, stepSize);
					x += stepSize;
					map.addV(x, y, 0);
					break;
				case 3:
					map.addV(x, y, stepSize);
					y += stepSize;
					map.addH(x, y, 0);
					break;
			}

			double[] pTemp = { x, y };
			points.add(pTemp);
			rend.setWalkTail(points.toArray(new double[0][0]));

			lastDir = (stepDir + 2) % 4;
			dirMask = 15 & ~(1 << lastDir);
			step++;
			length += stepSize;
			stepSize = stepSizes[++i % sLength];
		}
		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {

				double[] pTemp = { x, y };
				points.add(pTemp);
				rend.setWalkTail(points.toArray(new double[0][0]));

				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				stepSize = stepSizes[++i % sLength];
			}
		}
		x = xInit;
		y = yInit;
		i = stepSizes.length - 1;
		lastDir = (initStepDir + 2) % 4;
		dirMask = 15 & ~(1 << lastDir);

		ArrayDeque<double[]> points2 = new ArrayDeque<double[]>();
		double[] p2 = { x, y };
		points2.add(p2);
		rend.setWalkHead(points2.toArray(new double[0][0]));

		while (dirMask != 0) {
			loop: while (dirMask != 0) {
				stepDir = random(lastDir, dirMask, r, 4);
				switch (stepDir) {
					case 0:
						if (!map.intersectV(x, y, -stepSize)) {
							map.addH(x, y, -stepSize);
							x -= stepSize;
							break loop;
						}
						break;
					case 1:
						if (!map.intersectH(x, y, -stepSize)) {
							map.addV(x, y, -stepSize);
							y -= stepSize;
							break loop;
						}
						break;
					case 2:
						if (!map.intersectV(x, y, stepSize)) {
							map.addH(x, y, stepSize);
							x += stepSize;
							break loop;
						}
						break;
					case 3:
						if (!map.intersectH(x, y, stepSize)) {
							map.addV(x, y, stepSize);
							y += stepSize;
							break loop;
						}
						break;
				}
				// only when no step was taken
				dirMask &= ~(1 << (stepDir));
			}
			if (dirMask != 0) {

				double[] pTemp = { x, y };
				points2.add(pTemp);
				rend.setWalkHead(points2.toArray(new double[0][0]));

				lastDir = (stepDir + 2) % 4;
				dirMask = 15 & ~(1 << lastDir);
				step++;
				length += stepSize;
				i = (i + sLength - 1) % sLength;
				stepSize = stepSizes[i];
			}
		}
		Stats stats = new Stats();
		stats.pathLength = length;
		stats.stepLength = step;
		stats.endLength = Math.sqrt((x - xInit) * (x - xInit) + (y - yInit) * (y - yInit));
		stats.manEndLength = Math.abs(x - xInit) + Math.abs(y - yInit);
		return stats;
	}

	private final int random(final int lastDir, final int dirMask, final Random r, final int dim) {
		int rand, stepDir;
		do {
			rand = r.nextInt(dim - 1);
			stepDir = (lastDir + rand + 1) % dim;
		} while (((1 << stepDir) & dirMask) == 0);
		return stepDir;
	}

	private final int random(final int lastDir, final int dirMask, final MersenneTwisterFast r, final int dim) {
		int rand, stepDir;
		do {
			rand = r.nextInt(dim - 1);
			stepDir = (lastDir + rand + 1) % dim;
		} while (((1 << stepDir) & dirMask) == 0);
		return stepDir;
	}

}
